﻿ #region Disclaimer/Info
 
 /////////////////////////////////////////////////////////////////////////////////////////////////
 //
 //   File:		RoleProvider.cs
 //   Website:		http://dexterblogengine.com/
 //   Authors:		http://dexterblogengine.com/About.ashx
 //   Rev:		1
 //   Created:		19/01/2011
 //   Last edit:		19/01/2011
 //   License:		GNU Library General Public License (LGPL)
 // 
 //   For updated news and information please visit http://dexterblogengine.com/
 //   Dexter is hosted to Codeplex at http://dexterblogengine.codeplex.com
 //   For any question contact info@dexterblogengine.com
 //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #endregion

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Linq;
using Dexter.Dependency;
using Dexter.Domain.Model;
using Dexter.Domain.Results;
using Dexter.Logger.Contracts;
using Dexter.Services;

namespace Dexter.Web.Security {
	/// <summary>
	/// 	Custom role provider base on Deter <see cref = "IUserService" />. See also <seealso cref = "System.Web.Security.RoleProvider" />.
	/// </summary>
	public class RoleProvider : System.Web.Security.RoleProvider {
		/// <summary>
		/// 	The logger.
		/// </summary>
		protected readonly ILogger Logger;


		///<summary>
		///	All available Roles.
		///</summary>
		///<remarks>
		///	Be careful, because the collection of <see cref = "Role" /> is not populate, so read this property could cause more queries.
		///</remarks>
		protected IPagedResult <Role> Roles;

		/// <summary>
		/// 	An instance of a concrete implementation of <see cref = "IUserService" />
		/// </summary>
		protected readonly IUserService UserService;


		/// <summary>
		/// 	Initializes a new instance of the <see cref = "RoleProvider" /> class.
		/// </summary>
		public RoleProvider ( )
			: this ( DexterContainer.Resolve <ILogger> ( ) , DexterContainer.Resolve <IUserService> ( ) ) {}

		/// <summary>
		/// 	Initializes a new instance of the <see cref = "RoleProvider" /> class.
		/// </summary>
		/// <param name = "logger">The logger.</param>
		/// <param name = "userService">The user service.</param>
		public RoleProvider ( ILogger logger , IUserService userService ) {
			this.Logger = logger;
			this.UserService = userService;

			ReadAllRoles();
		}

		private void ReadAllRoles()
		{
			Roles = UserService.GetAllRoles ( 0 , 1000 );
		}

		/// <summary>
		/// 	Gets or sets the name of the application to store and retrieve role information for.
		/// </summary>
		/// <value></value>
		/// <returns>The name of the application to store and retrieve role information for.</returns>
		public override string ApplicationName {
			get { return "dexter"; }
			set { }
		}

		/// <summary>
		/// 	Initializes the provider.
		/// </summary>
		/// <param name = "name">The friendly name of the provider.</param>
		/// <param name = "config">A collection of the name/value pairs representing the provider-specific attributes specified in the configuration for this provider.</param>
		/// <exception cref = "T:System.ArgumentNullException">The name of the provider is null.</exception>
		/// <exception cref = "T:System.ArgumentException">The name of the provider has a length of zero.</exception>
		/// <exception cref = "T:System.InvalidOperationException">An attempt is made to call <see cref = "M:System.Configuration.Provider.ProviderBase.Initialize(System.String,System.Collections.Specialized.NameValueCollection)" /> on a provider after the provider has already been initialized.</exception>
		public override void Initialize ( string name , NameValueCollection config ) {
			if ( config == null ) {
				throw new ArgumentNullException ( "config" );
			}

			if ( string.IsNullOrEmpty ( name ) ) {
				name = "DexterRoleProvider";
			}

			if ( string.IsNullOrEmpty ( config[ "description" ] ) ) {
				config.Remove ( "description" );
				config.Add ( "description" , "Dexter Role Provider" );
			}
			base.Initialize ( name , config );
		}

		/// <summary>
		/// 	Gets a value indicating whether the specified user is in the specified role for the configured applicationName.
		/// </summary>
		/// <param name = "username">The user name to search for.</param>
		/// <param name = "roleName">The role to search in.</param>
		/// <returns>
		/// 	true if the specified user is in the specified role for the configured applicationName; otherwise, false.
		/// </returns>
		public override bool IsUserInRole ( string username , string roleName ) {
			try {
				User user = UserService.GetUserByUsername ( username );

				if ( user == null ) {
					throw new ProviderException ( "Unable to retrive the user." );
				}

				return user.IsInRole ( roleName );
			}
			catch ( Exception exception ) {
				throw new ProviderException ( "Unable to add userts to roles." , exception );
			}
		}

		/// <summary>
		/// 	Gets a list of the roles that a specified user is in for the configured applicationName.
		/// </summary>
		/// <param name = "username">The user to return a list of roles for.</param>
		/// <returns>
		/// 	A string array containing the names of all the roles that the specified user is in for the configured applicationName.
		/// </returns>
		public override string[] GetRolesForUser ( string username ) {
			try {
				User user = UserService.GetUserByUsername ( username );

				if ( user == null ) {
					throw new ProviderException ( "Unable to retrive the user." );
				}

				return user.Roles.Select ( x => x.Name ).ToArray ( );
			}
			catch ( Exception exception ) {
				throw new ProviderException ( "Unable to retrive the user roles." , exception );
			}
		}

		/// <summary>
		/// 	Adds a new role to the data source for the configured applicationName.
		/// </summary>
		/// <param name = "roleName">The name of the role to create.</param>
		public override void CreateRole ( string roleName ) {
			// throws ane xprection if the role already exists
			if (RoleExists(roleName))
				throw new ApplicationException("The role already exists");
			// creates the new role and add it to the lists
			Role role = new Role(roleName);
			UserService.Save(role);
			// read the role list again
			ReadAllRoles();
		}

		/// <summary>
		/// 	Removes a role from the data source for the configured applicationName.
		/// </summary>
		/// <param name = "roleName">The name of the role to delete.</param>
		/// <param name = "throwOnPopulatedRole">If true, throw an exception if <paramref name = "roleName" /> has one or more members and do not delete <paramref name = "roleName" />.</param>
		/// <returns>
		/// 	true if the role was successfully deleted; otherwise, false.
		/// </returns>
		public override bool DeleteRole ( string roleName , bool throwOnPopulatedRole ) {
			throw new NotImplementedException ( );
		}

		/// <summary>
		/// 	Gets a value indicating whether the specified role name already exists in the role data source for the configured applicationName.
		/// </summary>
		/// <param name = "roleName">The name of the role to search for in the data source.</param>
		/// <returns>
		/// 	true if the role name already exists in the data source for the configured applicationName; otherwise, false.
		/// </returns>
		public override bool RoleExists ( string roleName ) {
			roleName = roleName.ToLowerInvariant ( );

			return UserService.RoleExists ( roleName );
		}

		/// <summary>
		/// 	Adds the specified user names to the specified roles for the configured applicationName.
		/// </summary>
		/// <param name = "usernames">A string array of user names to be added to the specified roles.</param>
		/// <param name = "roleNames">A string array of the role names to add the specified user names to.</param>
		public override void AddUsersToRoles ( string[] usernames , string[] roleNames ) {
			IList <Role> rls = new Collection <Role> ( );

			foreach ( string roleName in roleNames ) {
				if ( string.IsNullOrEmpty ( roleName ) ) {
					continue;
				}
				
				string name = roleName.ToLowerInvariant ( );
				var r = Roles.Result.Where ( x => x.LoweredName == name ).FirstOrDefault ( );

				if ( r != null ) {
					rls.Add ( r );
				}
			}

			foreach ( string username in usernames ) {
				var user = UserService.GetUserByUsername ( username );

				if ( user == null ) {
					throw new ProviderException ( "Unable to retrive the user." );
				}


				rls.ForEach ( x => {
				              	if ( !user.Roles.Contains ( x ) ) {
				              		user.Roles.Add ( x );
				              	}
				              } );
			}
		}

		/// <summary>
		/// 	Removes the specified user names from the specified roles for the configured applicationName.
		/// </summary>
		/// <param name = "usernames">A string array of user names to be removed from the specified roles.</param>
		/// <param name = "roleNames">A string array of role names to remove the specified user names from.</param>
		public override void RemoveUsersFromRoles ( string[] usernames , string[] roleNames ) {
			IList <Role> rls = new Collection <Role> ( );

			foreach ( string roleName in roleNames ) {
				if ( string.IsNullOrEmpty ( roleName ) ) {
					continue;
				}

				string name = roleName.ToLowerInvariant ( );
				var r = Roles.Result.Where ( x => x.LoweredName == name ).FirstOrDefault ( );

				if ( r != null ) {
					rls.Add ( r );
				}
			}

			foreach ( string username in usernames ) {
				var user = UserService.GetUserByUsername ( username );

				if ( user == null ) {
					throw new ProviderException ( "Unable to retrive the user." );
				}

				rls.ForEach ( x => {
				              	if ( user.Roles.Contains ( x ) ) {
				              		user.Roles.Remove ( x );
				              	}
				              } );
			}
		}

		/// <summary>
		/// 	Gets a list of users in the specified role for the configured applicationName.
		/// </summary>
		/// <param name = "roleName">The name of the role to get the list of users for.</param>
		/// <returns>
		/// 	A string array containing the names of all the users who are members of the specified role for the configured applicationName.
		/// </returns>
		public override string[] GetUsersInRole ( string roleName )
		{
			var users = UserService.GetUsersInRole(roleName);
			return users.Select(u => u.Username).ToArray();
		}

		/// <summary>
		/// 	Gets a list of all the roles for the configured applicationName.
		/// </summary>
		/// <returns>
		/// 	A string array containing the names of all the roles stored in the data source for the configured applicationName.
		/// </returns>
		public override string[] GetAllRoles ( ) {
			return Roles.Result.Select ( x => x.Name ).ToArray ( );
		}

		/// <summary>
		/// 	Gets an array of user names in a role where the user name contains the specified user name to match.
		/// </summary>
		/// <param name = "roleName">The role to search in.</param>
		/// <param name = "usernameToMatch">The user name to search for.</param>
		/// <returns>
		/// 	A string array containing the names of all the users where the user name matches <paramref name = "usernameToMatch" /> and the user is a member of the specified role.
		/// </returns>
		public override string[] FindUsersInRole ( string roleName , string usernameToMatch ) {
			throw new NotImplementedException ( );
		}
	}
}
